/*
 * RouteDLS.java
 *
 * Created on February 22, 2007, 8:14 PM
 *
 * Copyright 2008 David D. Emory
 * 
 * This file is part of Five Points. See <http://www.fpdev.org> for
 * additional information regarding the project.
 * 
 * Five Points is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Five Points is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with Five Points.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.fpdev.core.algo;

import org.fpdev.core.transit.SubRouteInfo;
import org.fpdev.core.basenet.BLink;
import org.fpdev.core.basenet.BNode;
import org.fpdev.core.transit.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.fpdev.core.*;
import org.fpdev.core.basenet.*;

/**
 * An implementation of DepthLimitedSearch that records any transit
 * board/alight opportunites for the departing/arriving trip end. Any such
 * subroutes encountered are stored in a dictionary.   
 *
 * @author demory
 */
public class RouteDLS extends DepthLimitedSearch {

  private FPEngine engine_;
  private Map<String, SubRouteInfo> subLookup_;
  private int mode_,  option_;
  private List<BNode> recNodes_;
  private BNode isectNode_;
  public static final int MODE_ARRIVING = 1;
  public static final int MODE_DEPARTING = 2;
  public static final int OPTION_NONE = 0;
  public static final int OPTION_RECORDVISITED = 1;
  public static final int OPTION_CHECKVISITED = 2;

  public RouteDLS(FPEngine engine, int type, int limit, int mode, int option) {
    super(engine.getBaseNet().getGraph(), type, limit);
    engine_ = engine;
    mode_ = mode;
    option_ = option;
    subLookup_ = new HashMap<String, SubRouteInfo>();
    if (option == OPTION_RECORDVISITED) {
      recNodes_ = new LinkedList<BNode>();
    }
    isectNode_ = null;
  }

  public List<BNode> getRecordedNodes() {
    return recNodes_;
  }

  public void setRecordedNodes(List<BNode> nodes) {
    recNodes_ = nodes;
  }

  public BNode getIsectNode() {
    return isectNode_;
  }

  /*public int weight(CLink link, CNode inNode) {
  //return link.getLengthFeet();
  return (int) engine_.getBaseNet().getGraph().getEdgeWeight(link, inNode);
  }*/
  @Override
  public void nodeVisited(BNode node) {
    Iterator<SubRouteInfo> sris = null;
    switch (option_) {
      case OPTION_RECORDVISITED:
        recNodes_.add(node);
        break;
      case OPTION_CHECKVISITED:
        if (isectNode_ == null && recNodes_.contains(node)) {
          isectNode_ = node;
        }
        break;
    }
    switch (mode_) {
      case MODE_DEPARTING:
        sris = engine_.getOutSubRouteInfo(node);
        break;
      case MODE_ARRIVING:
        sris = engine_.getInSubRouteInfo(node);
        break;
    }
    //System.out.println("nv: "+node.getDisplayText());
    while (sris.hasNext()) {
      SubRouteInfo subInfo = (sris.next()).clone();
      //System.out.println("  sri="+subInfo.getSubRoute().getFullID());
      String id = subInfo.getSubRoute().getFullID(); //subInfo.getSubRoute().getRouteLinkFromDLI(subInfo.getDLI()).getID();
      if (!subLookup_.containsKey(id)) {
        //System.out.println("  contains key");
        subInfo.setAccessNode(node);
        subInfo.setDistance((double) distanceTo(node));
        subLookup_.put(id, subInfo);
      } else {
        SubRouteInfo foundSRI = subLookup_.get(id);
        if (foundSRI.getAccessNode() == node && foundSRI.getDLI() != subInfo.getDLI()) {
          //System.out.println("  SPECIAL CASE");
          subInfo.setAccessNode(node);
          subInfo.setDistance((double) distanceTo(node));
          subLookup_.put(id, subInfo);
        }
      }
    }
  //System.out.println(" end nodeVisited");
  }

  private int distanceTo(BNode node) {
    BNode curNode = node;
    int dist = 0;
    while (curNode != lastSource_) {
      BLink link = getParentLink(curNode);
      dist += link.getLengthFeet();
      curNode = graph_.opposite(curNode, link);
    }
    return dist;
  }

  public Map<String, SubRouteInfo> getSRILookup() {
    return subLookup_;
  }
}
